// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DBExecutionContextHelpers.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.DataModel
{
    #region

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Threading;

    using Org.OpenTrader.Framework.Enums;
    using Org.OpenTrader.Framework.LiveObjects;
    using Org.OpenTrader.Framework.LiveObjects.Interfaces;

    #endregion

    /// <summary>
    /// The db execution context helpers.
    /// </summary>
    public class DBExecutionContextHelpers
    {
        #region Constants and Fields

        /// <summary>
        /// The identity.
        /// </summary>
        private static readonly Identity Identity = Identity.Create(MethodBase.GetCurrentMethod());

        #endregion

        #region Public Methods

        /// <summary>
        /// The create.
        /// </summary>
        /// <param name="name">
        /// The name.
        /// </param>
        /// <returns>
        /// The create.
        /// </returns>
        public static bool Create(string name)
        {
            // Create the context
            var ctx = DBExecutionContext.CreateDBExecutionContext(Guid.NewGuid(), name);

            // Create the default container
            var ctn = DBContainer.CreateDBContainer(Guid.NewGuid(), "Default");

            // Add the container to context
            ctx.DBContainer.Add(ctn);

            // Add
            Singleton<DBFramework>.Instance.Entities.AddToDBExecutionContext(ctx);

            // Save
            var n = Singleton<DBFramework>.Instance.Commit();

            return n > 0;
        }

        /// <summary>
        /// The destroy.
        /// </summary>
        /// <param name="id">
        /// The id.
        /// </param>
        /// <returns>
        /// The destroy.
        /// </returns>
        public static bool Destroy(Guid id)
        {
            var query = from r in Singleton<DBFramework>.Instance.Entities.DBExecutionContext where r.ID == id select r;

            if (query.Count() > 0)
            {
                var dbContext = query.First();

                // Delete the context and all sub items (cascade)
                Singleton<DBFramework>.Instance.Entities.DeleteObject(dbContext);

                var n = Singleton<DBFramework>.Instance.Commit();

                return n > 0;
            }

            return false;
        }

        /// <summary>
        /// The get application.
        /// </summary>
        /// <param name="contextId">
        /// The contextId.
        /// </param>
        /// <returns>
        /// </returns>
        public static DBLiveObject GetApplication(Guid contextId)
        {
            var query = from r in Singleton<DBFramework>.Instance.Entities.DBLiveObject
                        where r.Type.Equals("A")
                        where r.DBExecutionContextID == contextId
                        select r;

            if (query.Count() == 1)
            {
                return query.First();
            }

            return null;
        }

        /// <summary>
        /// Get the containers associated to an execution context. If "safe" is given t
        /// </summary>
        /// <param name="executionContextId">
        /// </param>
        /// <param name="includeSafeContainers">
        /// The includeSafeContainers.
        /// </param>
        /// <returns>
        /// </returns>
        public static IList<DBContainer> GetContainers(Guid executionContextId, bool includeSafeContainers)
        {
            IList<DBContainer> list = null;

            if (includeSafeContainers)
            {
                var query = from r in Singleton<DBFramework>.Instance.Entities.DBContainer
                            where r.DBExecutionContext.ID == executionContextId
                            select r;

                if (query.Count() > 0)
                {
                    list = new List<DBContainer>(query.ToArray());
                }
            }
            else
            {
                var query = from r in Singleton<DBFramework>.Instance.Entities.DBContainer
                            where r.DBExecutionContext.ID == executionContextId
                            where r.IsStrong == false
                            select r;

                if (query.Count() > 0)
                {
                    list = new List<DBContainer>(query.ToArray());
                }
            }

            return list;
        }

        /// <summary>
        /// The get execution context.
        /// </summary>
        /// <param name="contextName">
        /// The contextName.
        /// </param>
        /// <returns>
        /// </returns>
        public static DBExecutionContext GetExecutionContext(string contextName)
        {
            var query = from r in Singleton<DBFramework>.Instance.Entities.DBExecutionContext
                        where r.Name == contextName
                        select r;

            if (query.Count() == 1)
            {
                return query.First();
            }

            return null;
        }

        /// <summary>
        /// The get execution contexts.
        /// </summary>
        /// <param name="logger">
        /// The logger.
        /// </param>
        /// <returns>
        /// </returns>
        public static IDictionary<string, DBExecutionContext> GetExecutionContexts()
        {
            IDictionary<string, DBExecutionContext> result = new Dictionary<string, DBExecutionContext>();

            var query = from r in Singleton<DBFramework>.Instance.Entities.DBExecutionContext select r;

            foreach (var executionContext in query)
            {
                result.Add(executionContext.Name, executionContext);
            }

            return result;
        }

        /// <summary>
        /// The load.
        /// </summary>
        /// <param name="contextName">
        /// The context name.
        /// </param>
        /// <param name="application">
        /// The application.
        /// </param>
        public static void Load(string contextName, out IApplication application)
        {
            var ctx = GetExecutionContext(contextName);
            if (ctx == null)
            {
                application = null;
                return;
            }

            var dbApplication = GetApplication(ctx.ID);

            ctx.DBContainer.Load();
            foreach (var c in ctx.DBContainer)
            {
                // Get the resources of the container
                var resources = DBContainerHelpers.GetResources(c.ID);

                var dir = string.Format(
                    "{0}\\OpenTrader\\{1}\\{2}", Path.GetTempPath(), Process.GetCurrentProcess().Id, c.ID);

                ILiveObjectContainerDescriptor desc = null;
                if (c.Name.Equals("Default"))
                {
                    // Use Default AppDomain (no isolation)
                    desc = new LiveObjectContainerDescriptor(
                        ELiveObjectContainerType.DefaultContainer, 
                        c.ID, 
                        c.Name, 
                        dir, 
                        resources, 
                        Singleton<LiveObjectsRegistry>.Instance);
                }
                else if (c.IsStrong.HasValue)
                {
                    if (c.IsStrong.Value)
                    {
                        // Use a separate process to isolate (strong isolation)
                        desc = new LiveObjectContainerDescriptor(
                            ELiveObjectContainerType.ProcessIsolationContainer, 
                            c.ID, 
                            c.Name, 
                            dir, 
                            resources, 
                            Singleton<LiveObjectsRegistry>.Instance);
                    }
                    else if (!c.IsStrong.Value)
                    {
                        // Use a seperate AppDomain to isolate (weak isolation)
                        desc = new LiveObjectContainerDescriptor(
                            ELiveObjectContainerType.AppDomainIsolationContainer, 
                            c.ID, 
                            c.Name, 
                            dir, 
                            resources, 
                            Singleton<LiveObjectsRegistry>.Instance);
                    }
                }

                // Create the container
                Singleton<LiveObjectsRegistry>.Instance.CreateContainer(desc);
            }

            // Create Application
            application = Singleton<LiveObjectsRegistry>.Instance.CreateLocalApplication(dbApplication, contextName);
        }

        /// <summary>
        /// The unload.
        /// </summary>
        public static void Unload()
        {
            // Cleanup
            var containers = Singleton<LiveObjectsRegistry>.Instance.Containers.ToArray();
            foreach (var container in containers)
            {
                Devel.NoThrow(() => Singleton<LiveObjectsRegistry>.Instance.DestroyContainer(container));
                Thread.Sleep(500);
            }

            Thread.Sleep(1000);
        }

        #endregion
    }
}